// Clippers.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "ReadDelMapping.h"
#include "ReadsMapping.h"
#include "Genome_Index_TableQ.h"
#include "LongReadsSet.h"
#include "ReadInBitsSet.h"
#include "ReadInBits.h"
#include "ParameterList.h"
#include "TestClippers.h"
#include "Filename.h"
#include "chdir.h"
#include "stdafx.h"
#include <string>
#include <vector>
#include <iostream>
using namespace std;

time_t startt, endt;

bool retriveReadSetsAndSettings\
(ParameterList& P, vector<string>& readSetsList)
{
    // (1) check reference
    if (!P.checkRefValidity()) {
        return(false);
    }
    // (2) check reads files
    P.bMappedSOLiDRead = is_colorspace_reads(P.readsFileFormat);
    if (getReadSetsFilenames(P, readSetsList)) {
        if (readSetsList.size() > 0) {
            const char* firstReadSetFileName = readSetsList.at(0).c_str();
            P.bMappedSOLiDRead = P.bMappedSOLiDRead || is_colorspace_reads(firstReadSetFileName);
            char cExpReadFormat = getReadsFileFormatSymbol(P.readsFileFormat);
            P.readLength = getReadLength(firstReadSetFileName, cExpReadFormat);
        } else {
            LOG_INFO("Info %d: Cannot get read set from the read list.\n", ERROR_LOG);
            return(false);
        }
    } else if (atoi(P.readsFile) >= 25) {
        P.readLength = atoi(P.readsFile);
    } else if (atoi(P.readsFile) > 0) {
        LOG_INFO("Info %d: Incorrect reads file or the anchor length is too short\n", ERROR_LOG);
        return(false);
    } else {
        // can not open the read file
        return(false);
    }
    // truncated and filter reads according to options
    P.truncatReadLength();
    if (P.bFileterDummyRead) {
        if (P.filteredDummyBases == 0) {
            P.filteredDummyBases = P.readLength;
        }
    }
    if (P.bFileterRepeatRead) {
        if (P.filteredRepeatBases == 0) {
            P.filteredRepeatBases = P.readLength;
        }
    }

    P.anchorLength = getAnchorLength(P.readLength, P.anchorLength);
    if (P.readLength <= 2 * P.anchorLength) {
        LOG_INFO("Info %d: Read length is too short for the specified anchor length\n", ERROR_LOG);
        return(false);
    }
    // (3) select seed according to the setting
    P.seedId = selectSeed(P);
    // (4) avoid to use the same name
    if (readSetsList.size() > 1)
        P.outputFileN[0] = '\0';
    return(true);
}

string get_Index_Path(ParameterList P)
{
    if (hasTheExtName(P.refFile, ".index") || P.refFormat == "index" ) {
        return(string(P.refFile));
    } else {
        string indexPath = default_index_path(getBasename\
                                              (P.refFile), P.bMappedSOLiDRead, P.seedId, P.anchorLength);
        return(indexPath);
    }
}

bool buildIndexTable(CGenome_Index_TableQ& indexTable, ParameterList& P)
{
    if (P.refFormat == "index" || hasTheExtName(P.refFile, ".index")) {
        LOG_INFO("\nInfo %d: Index file %s has incorrect format\n", ERROR_LOG, P.refFile);
        return(false);
    } else if (indexTable.getSeqFromFasta(P.refFile, P.refFormat)) {
        bool makeIndexSuccessfully =\
            indexTable.make_index_table(P.anchorLength, P.seedId, P.bMappedSOLiDRead, P.bMaskedMathRepeat);
        if(makeIndexSuccessfully) { 
            if (P.bSaveIndex) {
                const bool printErrorMsg = true;
                indexTable.save_index_table(P.indexFileN, printErrorMsg);
            }
            indexTable.bExcludeAmbiguous = P.bExcludeAmbiguousReads;
            indexTable.uiSubDiffThreshold = P.seedId; // only allow 0, 1, 2, 3 in the seed
            return(true);
        }
    }
    return(false);
}

CReadsSetParam passOptions(const MappingOpts& P)
{
    CReadsSetParam opt;
    opt.bDiscardReadsWN = P.bDiscardReadWithN;
    strcpy(opt.fileFormat, P.outputFormat);
    opt.filteredDummyBases = P.filteredDummyBases;
    opt.filteredRepeatBases = P.filteredRepeatBases;
    opt.uiReadLength = P.readLength;
    return(opt);
}

int getDelAlignmentsInParallel(vector<string> readSetsList, CGenome_Index_TableQ& indexTable, MappingOpts P)
{
    P.clearOutputFileName(readSetsList.size() > 1);
    //__OPENMP_FOR_PARALLEL__(#pragma)
#ifdef _OPENMP
    int numberOfCPUs = omp_get_num_procs();
    std::cout << numberOfCPUs << " CPUs." << BLANK_LINE << endl;
#pragma omp parallel for
#endif
    for (int i = 0; i < (int)readSetsList.size(); i++) {
        CReadsDelMapping mapping(P);
        const char* readSetName = (readSetsList.at(i)).c_str();
        if (checkFileExist(readSetName)) {
            CReadsSetParam opt = passOptions(P);
            CLongReadsSet longReadSet(readSetName, opt);
            indexTable.bExcludeAmbiguous = false;
            TIME_INFO(mapping.getDelAlignments(indexTable, longReadSet), "Mapping takes");
        }
    }
    return(0);
}

void generateTestReadsFromRef(ParameterList& P, vector<string>& readSetsList)
{
    char testReadFile[FILENAME_MAX];
    sprintf(testReadFile, "%d_%s", P.anchorLength, P.refFile);
    if (hasTheExtName(P.readsFile, ".fq") || hasTheExtName(P.readsFile, ".fastq")) {
        chExtName(testReadFile, "_test.fq");
    } else {
        chExtName(testReadFile, "_test.fq");
    }
    simSpliceReads(testReadFile, P.refFile, P.readLength, P.anchorLength);
    strcpy(P.readsFile, testReadFile);
    readSetsList.clear();
    readSetsList.push_back(P.readsFile);
}

/*
 * The main function construct or read in the index of a set of reference genome.
 * It maps the reads
 */
int main(int argc, const char* argv[])
{
    // (1) Get parameters.
    ParameterList P = getParameterList(argc, argv);
    if (argc < 2) return 0;
    vector<string> readSetsList;
    bool validSetting = retriveReadSetsAndSettings(P, readSetsList);
    if (!validSetting) {
        LOG_INFO("\nInfo %d: Invalid or confusing command. Check the opt\n", CONFIG_LOG);
#ifdef WIN32
        STRIKE_KEY2CONTINUE;
#endif
        return(-1);
    }
    P.anchorLength = getAnchorLength(P.readLength, P.anchorLength);
    if (P.bTest) { // Generate test reads
        generateTestReadsFromRef(P, readSetsList);
    }
    // (2) Build or read index table
    CGenome_Index_TableQ indexTable;
    string indexPath = get_Index_Path(P).c_str();
    const bool printErrMsg = false;
    if (P.bMakeIndex || indexTable.read_index_table(indexPath.c_str(), printErrMsg) == false) {
        if (buildIndexTable(indexTable, P) == false) {
            LOG_INFO("\nInfo %d: Fail to build index\n", ERROR_LOG);
            return(-1);
        }
    }
    P.anchorLength = indexTable.uiRead_Length;
    P.seedId = indexTable.chosenSeedId;
    // (3) Mapped reads.
    TIME_INFO(getDelAlignmentsInParallel(readSetsList, indexTable, P),\
              "Mapping single end long reads");
    return 0;
}
